/**
    Copyright (C) 2011  Alexander Vollmer

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.jefb.dao.impl;

import java.io.Serializable;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityNotFoundException;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType;
import javax.persistence.Table;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.jefb.dao.ICommonDao;
import org.jefb.entity.CommonSearchCriteria;
import org.springframework.stereotype.Repository;

@Repository
public class CommonDao implements ICommonDao {

	private static final long serialVersionUID = 1L;

	protected EntityManager entityManager;

	@PersistenceContext(type=PersistenceContextType.EXTENDED)
	public void setEntityManager(EntityManager entityManager) {
		this.entityManager = entityManager;
	}

	/**
	 * Called to list a objects of a given type within the databasec
	 * 
	 */
	@SuppressWarnings("unchecked")
	public <T> List<T> findAll(Class<T> clazz) {
		if(clazz.isAnnotationPresent(Table.class)){
			Table table = clazz.getAnnotation(Table.class);
			return (List<T>) entityManager.createNativeQuery("select * from "+table.name(), clazz).getResultList();
		}else{
			//TODO handle it
			return null;
		}
	}

	/**
	 *
	 */
	public <T> T find(Class<T> clazz, Serializable id)
			throws EntityNotFoundException {
		T result = entityManager.find(clazz, id);
		if (result == null) {
			throw new EntityNotFoundException("Entity " + clazz.getName()
					+ " with id " + id + " not found.");
		}
		return result;
	}

	/**
	 * Called to delete an object from the database
	 * 
	 */
	public <T> T remove(Class<T> clazz, Serializable id)
			throws EntityNotFoundException {
		T instance = find(clazz, id);
		entityManager.remove(instance);
		return instance;
	}

	/**
	 * Called to add an object to the database
	 * 
	 */
	public void persist(Object entity) {
		entityManager.persist(entity);
	}

	/**
	 * Update an object within the database.
	 */
	public void merge(Object entity) {
		entityManager.merge(entity);
		//getSession().saveOrUpdate(entity);
	}
	
	/**
	 * Loads objects from database by given criteria.
	 * @param <T>
	 * @param clazz of object
	 * @param criterion. Criteria for search.
	 * @return List of found objects.
	 */
	@SuppressWarnings("unchecked")
	protected <T> List<T> findByCriteria(Class<T> clazz, Criterion... criterion) {
		Criteria criteria = getSession().createCriteria(clazz);
		for (Criterion c : criterion) {
			criteria.add(c);
		}
		return criteria.list();
	}

	/**
	 * Find entities by given example.
	 */
	@SuppressWarnings("unchecked")
	public <T> List<T> findByExample(T exampleInstance, String... excludeProperty) {
		Criteria criteria = getSession().createCriteria(exampleInstance.getClass());
		Example example = Example.create(exampleInstance);
		for (String ep : excludeProperty) {
			example.excludeProperty(ep);
		}
		criteria.add(example);
		return criteria.list();
	}

	public Session getSession() {
		return (Session) entityManager.getDelegate();
	}
	
	/**
	 * Flush the session. All changes should be written into the database.
	 */
	public void flush(){
		getSession().flush();
	}
	
	/**
	 * Attach given entity to the persistence context.
	 */
	public <T> T refresh(T entity){
		getSession().refresh(entity);
		return entity;
	}
	
	@SuppressWarnings({ "unchecked"})
	public <T> List<T> findByCriteria(T object, CommonSearchCriteria commonCriteria) {
		Criteria dQuery = createCommonCriteriaQuery(object);
		if ("DESC".equals(commonCriteria.getSortDirection())) {
			dQuery.addOrder(Order
					.desc(commonCriteria.getSortField()));
		}
		if ("ASC".equals(commonCriteria.getSortDirection())) {
			dQuery.addOrder(Order
					.asc(commonCriteria.getSortField()));
		}
		dQuery.setFirstResult(commonCriteria.getPagingStart());
		dQuery.setMaxResults(commonCriteria.getPageSize());
		return dQuery.list();
	}

	private Criteria createCommonCriteriaQuery(Object object) {
		Criteria dQuery = getSession().createCriteria(object.getClass());
		return dQuery;
	}

	public Long getTotalSizeByCriteria(Object searchCriteria) {
		Criteria dQuery = createCommonCriteriaQuery(searchCriteria);
		dQuery.setProjection(Projections.rowCount());
		return (Long)dQuery.uniqueResult();
	}
	
	
}
